Next | Prev | Up | Top | Contents | Index

Dynamic Resource Allocation

Rather than bind the interrupt IRQ input and a specific channel for DMA to a specific VECTOR: entry at lboot time, the driver allocates these resources dynamically. In the case of interrupts, there are three parts to the process:

  1. A specific IRQ number is obtained from the system on a "first come first served" basis through a call to eisa_ivec_alloc(). The mask parameter passed in its 16-bit bitmap vector specifies which IRQ choices are acceptable for this device hardware. This is necessary because most EISA cards can only be programmed to respond to a limited number of IRQ possibilities. In the case of an ISA card, which is jumpered to only respond to a single IRQ input, the vector only has a single bit "on" to specify this particular setting. Multiple cards can be assigned the same IRQ, although, by default, the allocation routine tries to assign an unused interrupt input. The allocation routine only returns failure if the driver requests a vector for a mask, and all mask choices are already programmed to respond to an incompatible choice for edge- or level-sensitive interrupts. Each IRQ can respond to either edge- or level-sensitive interrupts, but not to both.

    The EISA interrupt priority level is used to specify the interrupt IRQ number for the device. Its range is IRQ0-IRQ15, excluding IRQ2, which is used to cascade the interrupt controllers. EISA directly associates the interrupt priority to the IRQ input line. See the Intel 82357 Specification or the EISA Technical Reference Guide for a description of the priority ordering scheme.

    There are two system resources that need to be reserved by some EISA/ISA drivers: interrupt request inputs (IRQ's) and DMA channels. Instead of pre-allocating these in the system file, a dynamic allocation scheme hands them out to the driver at initialization time. The driver is responsible for specifying which choices for IRQs and DMA channels are acceptable for the particular hardware. The operating system uses the appropriate allocation routine to assign them to the driver, which then uses the returned value to configure the hardware to respond appropriately.

  2. The card is programmed to generate its interrupt on the assigned IRQ. This is a device-specific procedure. ISA cards typically cannot change their IRQ through software; they usually have to be jumpered.

    The value must be selected from IRQ 3-7, 9-12, 14, and 15. All EISA interrupts are channeled into one CPU interrupt level. The priority of this CPU interrupt is below that of the clock and at the same level as on-board devices. Although the EISA interrupt is generated at the same level, it is serviced when the CPU services EISA interrupts. It services the EISA-bus interrupts in order of their EISA-bus priority, at least in that order when multiple devices interrupt at the same time.

  3. A specific routine and argument input is associated with the assigned IRQ. This is done through the call to eisa_ivec_set().
Once eisa_ivec_set() has been used to bind the selected IRQ to an interrupt handling routine, all interrupts generated to that IRQ cause the related handling routine to be called. Since multiple cards may be responsible for the interrupt, the driver is required to check its own hardware registers to test for the cause of the pending interrupt. The call is specified as:

eisa_ivec_set (vint_t adapter, int irq, void (func*)(), int arg);

func passes arg as an argument when called. Typically, arg corresponds to the ctlr number of the device.

The process for allocating a DMA channel is similar to that for obtaining an interrupt number. The call to the eisa_dmachan_alloc() routine uses an 8-bit mask to represent DMA channels zero through seven. Note that bit four must always be masked because that channel is unavailable for a card's case. The value returned by the channel allocation routine is then saved to program the device to transfer data on that particular channel, as specified in the section describing the EISA DMA utility functions.

The following example is a possible outline initialization routine for the EISA device module declared in the previous section about the system files:

#include <sys/types.h>
#include <sys/edt.h>
#include <sys/pio.h>
#include <sys/eisa.h>
#include <sys/cmn_err.h>

struct edrv_info {
   caddr_t e_addr[NBASE];
   int     e_dmachan;
} einfo[4];

#define CARD_ID        0x0163b30a
#define IRQ_MASK       0x0018
#define DMACHAN_MASK   0x7a

edrv_edtinit(edt_t *e)
{
   int iospace, eirq, edma_chan;
   struct edrv_info *einf;
   piomap_t *pmap;
   einf = &einfo[e->e_ctlr];

   /* map address spaces */
   for (iospace = 0; iospace < NBASE; iospace++) {
      if (!e->e_space[iospace].ios_iopaddr)
         break;

      pmap = pio_mapalloc(e->e_bus_type, e->e_adap,
         &e->e_space[iospace], PIOMAP_FIXED, "edrv");

      einf->e_addr[iospace] = pio_mapaddr(pmap,
         e->e_space[iospace].ios_iopaddr);
   }

      /* should mark not-present somewhere */
      return;
   }

   /* Initialize Interrupt Vector */
   eirq = eisa_ivec_alloc(e->e_adap, IRQ_MASK,
          EISA_EDGE_IRQ);
   if (eirq < 0) {
      cmn_err(CE_WARN,
      "edrv: ctlr %d could not allocate IRQ\n",
         e->e_ctlr);

      /* should mark unavailable */
      return;
   }

   eisa_ivec_set(e->e_adap, eirq, edrv_intr, e->e_ctlr);

   /* Allocate DMA Channel */
   edma_chan = eisa_dmachan_alloc(e->e_adap, DMACHAN_MASK);
   if (edma_chan < 0) {
      cmn_err(CE_WARN,
      "edrv: ctlr %d could not allocate DMA Chan\n",
         e->e_ctlr);

      /* should mark unavailable */
      return;
   }
   einf->e_dmachan = edma_chan;
   
}

Next | Prev | Up | Top | Contents | Index